#4 基础语法

#4.1def 关键字：全称是define，意为”定义”。

def attack(count): 
    """
        攻击
    :param count:次数 
    """
    for __ in range(count):
        print("直拳")
        print("摆拳")
        print("勾拳")
#4.2调用
attack(2)


#4.3 返回值

#return 数据 
#return后没有语句，相当于返回 None。
#函数体没有return，相当于返回None。

# 1. 调用者,可以接收也可以不接收返回值
def func01():
    print("func01执行了")
    return 100

result = func01()
print(result)#100


# 2.在Python语言中,
# 函数没有return或return后面没有数据,
# 都相当于return None
def func02():
    print("func02执行了")
    return

result = func02()
print(result)#None


# 3.return可以退出函数
def func03():
    print("1")
    return
    print("2")
func03()# 1

# 4. return 可以退出多层循环嵌套
def func04():
    while True:
        while True:
            while True:
                print("循环体")
                return
func04()

#5 可变／不可变类型在传参时的区别

#(3) 传参说明：
#不可变类型的数据传参时，函数内部不会改变原数据的值。
#可变类型的数据传参时，函数内部可以改变原数据。 

def func01(p1,p2):
    p1 = "孙悟空"
    p2["八戒"] += 50

a = "悟空"
b = {"八戒":100}

func01(a,b)
print(a)#悟空
print(b)#{'八戒': 150}


#6 函数参数
#6.1.1 位置实参  
# 根据顺序与形参进行对应
def func02(a1,a2,a3):
    print(a1)
    print(a2)
    print(a3)
func02(1,2,3)

# 6.1.2 序列实参   
# 用 * 将序列拆解后与形参的位置依次对应

def func03(a1,a2,a3):
    print(a1)
    print(a2)
    print(a3)
#列表
list01= [5,6,7]
func03(*list01)
tuple01=(10,11,12)
#字典
func03(*tuple01)
#字符串
str01 = "张三丰"
func03(*str01)
#张
#三
#丰

func03(*[110,111,112])
#110
#111
#112

#6.1.3 关键字实参
#根据名字与形参进行对应
def func04(a1,a2,a3):
    print(a1)
    print(a2)
    print(a3)

func04(a1=15,a2=16,a3=17)
#15
#16
#17


#6.1.4 字典关键字实参
#用**将字典拆解后与形参的名字进行对应。
#配合形参的缺省参数，可以使调用者随意传参。

def func05(a1,a2,a3):
    print(a1)
    print(a2)
    print(a3)
dict01 = {"a1":"b","a2":"c","a3":"d"}
func05(**dict01)
#b
#c
#d


#6.2形参定义方式parameter
#6.2.1 默认形参

def func07(p1=0,p2="--",p3=100):
    print("func07")
    print(p1)
    print(p2)
    print(p3)

func07(p2=2)
#0
#2
#100
# 支持同时使用位置实参与关键字实参
func07(1,p3=90)
#1
# --
#90
# 注意1:先位置实参,后关键字实参
# func01(p1 =1,2,3) # 错误

'''
练习：
定d义函数,根据小时、分钟、秒,计算总秒数
调用：提供小时、分钟、秒
调用：提供分钟、秒
调用：提供小时、秒
调用：提供分钟
'''
'''
def total_seconds(hour=0, minute=0, second=0):
    print("total_seconds")
    totalnum = 0
    if hour:
        hsecomd = hour*60*60
        totalnum += hsecomd
    if minute:
        msecomd = minute*60
        totalnum += msecomd
    totalnum += second
    return totalnum

total = total_seconds(1,12)
print(total,"秒")
try:
    h = int(input("请输入时"))
    print(h)
    m = int(input("请输入分"))
    s = int(input("请输入秒"))
    total = total_seconds(h,m,s)
    print(total,"秒")
except ValueError:
    print("输入的不是整数")
'''



#6.2.2 位置形参
#def 函数名(形参名1, 形参名2, ...):


#6.2.3 命名关键字形参
#def 函数名(*args, 命名关键字形参1, 命名关键字形参2, ...):
#        函数体
# def 函数名(*, 命名关键字形参1, 命名关键字形参2, ...):
# ​      函数体

# 作用：强制实参使用关键字传参 

# 命名关键字形参:
# 星号元组形参后面的位置形参
# 限制实参必须是关键字实参

def func08(*args,p1,p2):
    print(args)
    print(p1)
    print(p2)

func08(p1=1,p2=2)
#()
#1
#2
func08(1,2,3,p1=1,p2=2)
#(1, 2, 3)
#1
#2
func08([1,2,3],p1=1,p2=2)
#([1, 2, 3],)
#1
#2
#func08([1,2,3],1,2)#不是关键字传参，报错

#通常星号后面的命名关键字形参属于辅助参数,可选.
def func09(p1,*,p2=0):
    print("func09")
    print(p1)
    print(p2)

func09(1)
func09(1,p2=2)
func09(p1=1,p2=2)


def func10(p1,*args,p2=0):
    print("func10")
    print(p1)
    print(args)
    print(p2)

func10(1,2,3)
#1
#(2, 3)
#0
func10(1,p2=2)
#1
#()
#2
func10(p1=1,p2=2)
#1
#()
#2


#6.2.4 星号元组形参
#可以将多个位置实参合并为一个元组
#形参列表中最多只能有一个

def func03(*args):
    print(args)
    
func03()#()
func03(1,2,3,3)#(1, 2, 3, 3)
#func03(a=1)



#6.2.5 双星号字典形参


#def 函数名(**字典形参名):
#可以将多个关键字实参合并为一个字典

def func11(**kwargs):
    print(kwargs)
    
func11(a=1,b=2)
#{'a': 1, 'b': 2}

#func11(1,2) #只能是关键字实参
#func11([1,2]) #只能是关键字实参


#6.2.6 形参自左至右的顺序
#位置 --> 星号元组 --> 命名关键字 --> 双星号字典

def func12(a,*args,b, **kwargs):
    print(a)
    print(args)
    print(b)
    print(kwargs)
    
func12(1,5,6,b="dd",c=18,d=[12,13])
#1
#(5, 6)
#dd
#{'c': 18, 'd': [12, 13]}


def func01(list_target):
  print(list_target)# ?

def func02(*args):
  print(args)# ?

def func03(*args,**kwargs):
  print(args)# ?
  print(kwargs)# ?

def func04(p1,p2,*,p4,**kwargs):
  print(p1)# ?
  print(p2)# ?
  print(p4)# ?
  print(kwargs)# ?

func01([1,2,3])#[1, 2, 3]
func02(*[1,2,3])#(1, 2, 3)
func03(1,2,3,a=4,b=5,c=6) #(1, 2, 3)  {'a': 4, 'b': 5, 'c': 6}
func04(10,20,p4 = 30,p5 = 40)
#10
#20
#30
#{'p5': 40}


#7 作用域LEGB

#变量起作用的范围。

#7.2 分类
#(1) Local局部作用域：函数内部。
#(2) Enclosing 外部嵌套作用域 ：函数嵌套。
#(3) Global全局作用域：模块(.py文件)内部。
#(4) Builtin内置模块作用域：builtins.py文件。

#7.3 变量名的查找规则

# (1) 由内到外：L -> E -> G -> B
# 
# (2) 在访问变量时，先查找本地变量，
# 然后是包裹此函数外部的函数内部的变量，
# 之后是全局变量，最后是内置变量。
#就近原则

# 7.4 局部变量
# (1) 定义在函数内部的变量(形参也是局部变量)
# (2) 只能在函数内部使用
# (3) 调用函数时才被创建，函数结束后自动销毁


# 7.5 全局变量
# (1) 定义在函数外部,模块内部的变量。
# (2) 在整个模块(py文件)范围内访问（但函数内不能将其直接赋值）。


#7.6 global 语句

data02 = 20
data03 = [30]

def func13():
    data01 = 10
    print(data01)
    print(data02)

func13()
#10
#20

def func14():
    #print(data01) #不能使用局部变量
    print(data02)
func14()#20

def func15():
    data02 = 200
    print(data02)#200
    #此时并未修改全局变量data02，只是新建了同名变量

func15()
print(data02)#20  未修改全局变量

def func16():
    global data02 #修改了全局变量
    data02=200

func16()
print(data02)#200  修改了全局变量

def func17():
    data03[0] = 300 #全局变量是列表，修改值时不需要global

print(data03)#300  修改了全局变量
func17()


data010 = 10

def func01(p):
    global data010
    data010 += 1
    p += 1

data020 = 10
func01(data020)
#data010  11
#data020 10
func01(data020)
print(data010)  # data010  12
print(data020)  # data020  10


#作用域和命名空间示例

#scope  作用域
#local  局部
#nonlocal 非局部的  (提升局部变量的)
#global  全局的


def scope_text():
    def do_local():
        spam = "局部spam"

    def do_nonlocal():
        nonlocal spam
        spam = "上层spam"
    
    def do_global():
        global spam
        spam = "全局spam"

    spam = "测试局部spam"
    do_local()
    print("do_local:",spam)
    #do_local: 测试局部spam
    do_nonlocal()
    print("do_nonlocal:",spam)
    # do_nonlocal: 上层spam
    do_global()
    print("do_global:",spam)
    # do_global: 上层spam

scope_text()
print("scope_text:",spam)
# scope_text: 全局spam


